package pyrachnid.model.ground_model;

import java.awt.image.BufferedImage;
import java.util.ArrayList;
import pyrachnid.model.pathFinderModel.Noeud;
import pyrachnid.model.robot_model.Robot;

public class Map implements Cloneable {
	private Cell[][] _ground;
	private String _name;
	private int _Xdimension;
	private int _Ydimension;
	private double _Xsize;
	private double _Ysize;
	private ArrayList<Noeud> _noeuds;

	public Map(int Xdimension, int Ydimension, BufferedImage buffImage, String name) {
		this._name = name;
		this._ground = new Cell[Xdimension][Ydimension];
		this._Xdimension = Xdimension;
		this._Ydimension = Ydimension;
		this._Xsize = buffImage.getWidth();
		this._Ysize = buffImage.getHeight();
		_noeuds = new ArrayList<Noeud>();
		try {
			computeMatrice(buffImage);
		} catch (Exception e) {
			System.out.println("Impossible de générer les cellules");
			e.printStackTrace();
		}
	}
	
	@Override
	public Map clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		Map clonedMap = (Map) super.clone();
		clonedMap._ground = _ground.clone();
		for(int i = 0; i<= _ground.length -1 ; i++){
			Cell[] tmpArray = new Cell[_ground[i].length];
			for(int j = 0; j <= _ground[i].length -1; j++)
				tmpArray[j] = clonedMap._ground[i][j].clone();
			clonedMap._ground[i] = tmpArray;
		}
		clonedMap._noeuds = new ArrayList<Noeud>();
		return clonedMap;
	}

	public Cell[][] getGround() {
		return _ground;
	}
	
	public String getName() {
		return _name;
	}

	public void setName(String name) {
		this._name = name;
	}
	
	public int getXdimension() {
		return _Xdimension;
	}

	public void setXdimension(int Xdimension) {
		this._Xdimension = Xdimension;
	}
	
	public int getYdimension() {
		return _Ydimension;
	}

	public void setYdimension(int Ydimension) {
		this._Ydimension = Ydimension;
	}
	
	public double getXsize() {
		return _Xsize;
	}

	public void setXsize(double Xsize) {
		this._Xsize = Xsize;
	}
	
	public double getYsize() {
		return _Ysize;
	}

	public void setYsize(double Ysize) {
		this._Ysize = Ysize;
	}
	
	public ArrayList<Cell> getFires() {
		ArrayList<Cell> cellFire = new ArrayList<Cell>();
		
		for(int i = 0; i < _Xdimension; i++) {
			for(int j = 0; j < _Ydimension; j++) {
				Cell cellTmp = _ground[i][j];
				
				if(cellTmp.getIsInFire())
					cellFire.add(cellTmp);
			}
		}
		
		return cellFire;
	}
	
	public ArrayList<Cell> getCells() {
		ArrayList<Cell> cellFire = new ArrayList<Cell>();
		
		for(int i = 0; i < _Xdimension; i++) {
			for(int j = 0; j < _Ydimension; j++) {
				Cell cellTmp = _ground[i][j];
					cellFire.add(cellTmp);
			}
		}
		
		return cellFire;
	}
	
	private void computeMatrice (BufferedImage monImage) throws Exception {
		for (int i = 1; i <= this._Xdimension; i++)
		{
			for (int j = 1; j <= this._Ydimension; j++)
			{
				int groudValue = getGrayLevelForOneCase(i, j, monImage);
				_ground[i-1][j-1] = new Cell(i-1, j-1, groudValue);
			}
		}
	}
	
	private int getGrayLevelForOneCase(int coordX,int coordY, BufferedImage monImage)
	{		
		//Calcule taille en pixel d'une case
		int tailleCaseX = (int) (this._Xsize / this._Xdimension);
		int tailleCaseY = (int) (this._Ysize / this._Ydimension);
		
		//Donne le point de d�part d'une case en pixel
		int coordPixelX = tailleCaseX * coordX - tailleCaseX;
		int coordPixelY = tailleCaseY * coordY - tailleCaseY;
		
		//Calcule de la moyenne du niveau de gris
		ArrayList<Integer> listeValeursPixel = new ArrayList<Integer>();
		for (int i = coordPixelX; i < tailleCaseX + coordPixelX; i++)
		{
			for (int j = coordPixelY; j < tailleCaseY + coordPixelY; j++)
			{
				int valeurPixelBrut = monImage.getRGB(i,j);
				int valeurPixelNet = (valeurPixelBrut & 0xff);
				listeValeursPixel.add(valeurPixelNet);
			}
		}
		
		int sum = 0;
		for (int y = 0; y < listeValeursPixel.size(); y++)
		{
			sum += listeValeursPixel.get(y).intValue();
		}
		
		//Retournement de la valeur pour qu'il y est une difficult� croissante de 0 � 255 et non de 255 � 0
		return Math.abs((sum / listeValeursPixel.size()) - 255);
	}

	public Noeud getNoeud(Cell p_source) {
		for(int i = 0; i < _noeuds.size(); i++)
		{
			if(_noeuds.get(i).getCell().equals(p_source))
			{
				return _noeuds.get(i);
			}
		}
		return null;
	}
	
	public void generateNoeud(Cell p_target, Robot p_robot) {
		Noeud[][] noeud = new Noeud[_Xdimension][_Ydimension];
		_noeuds.clear();
		
		for(int i = 0; i < _Xdimension; i++)
		{
			for(int j = 0; j < _Ydimension; j++)
			{
				Cell curentCell = _ground[i][j];
				double value = 0.0;
				
				// On ne se d�place pas dans la derni�re case
				if(p_target != curentCell)
				{
					value = (curentCell.getDistanceTo(p_target) / 100) + curentCell.getGroundValue() * p_robot.getRobotType().getGroundInfluence();
				}
				
				noeud[i][j] = new Noeud(value, curentCell);	
				_noeuds.add(noeud[i][j]);
			}
		}

		for(int i = 0; i < _Xdimension; i++)
		{
			for(int j = 0; j < _Ydimension; j++)
			{
				generateNoeudDellegate(noeud, i, j, p_robot, p_target);
			}
		}
	}
	
	private void generateNoeudDellegate(Noeud[][] p_noeud, int p_x, int p_y, Robot p_robot, Cell p_target)
	{
		Noeud curentNoeud = p_noeud[p_x][p_y];
		int robotValue = p_robot.getRobotType().getMaxGroundValue();
		double valueMoove = curentNoeud.getCell().getGroundValue();
		
		if(!curentNoeud.getCell().getIsInFire() && valueMoove < robotValue)
		{
			// All direction
			for(int i = -1; i <= 1; i++) {
				for(int j = -1; j <= 1; j++) {
					
					// Not the curent cell
					if(!(i == 0 && j == 0))
					{
						int x = p_x+i;
						int y = p_y+j;
						
						// In the map
						if(x >= 0 && x < _Xdimension && y >= 0 && y < _Ydimension)
						{
							Cell cellTarget = p_noeud[x][y].getCell();
							int groundValue = cellTarget.getGroundValue();
							
							if((robotValue >= groundValue && (!cellTarget.getIsInFire())) || p_target.equals(cellTarget))
							{
								curentNoeud.add_noeud(valueMoove, p_noeud[x][y]);
							}
						}
					}
				}
			}
		}
	}
	
}